<!DOCTYPE html>
<html lang="en-us">

<head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>PG SHELL TEST</title>
    <style>
      .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
      textarea.emscripten { font-family: monospace; width: 80%; }
      div.emscripten { text-align: center; }
      div.emscripten_border { border: 1px solid black; }
      /* the canvas *must not* have any border or padding, or mouse coords will be wrong */
      canvas.emscripten { border: 0px none; background-color: black; }

      .spinner {
        height: 50px;
        width: 50px;
        margin: 0px auto;
        -webkit-animation: rotation .8s linear infinite;
        -moz-animation: rotation .8s linear infinite;
        -o-animation: rotation .8s linear infinite;
        animation: rotation 0.8s linear infinite;
        border-left: 10px solid rgb(0,150,240);
        border-right: 10px solid rgb(0,150,240);
        border-bottom: 10px solid rgb(0,150,240);
        border-top: 10px solid rgb(100,0,200);
        border-radius: 100%;
        background-color: rgb(200,100,250);
      }
      @-webkit-keyframes rotation {
        from {-webkit-transform: rotate(0deg);}
        to {-webkit-transform: rotate(360deg);}
      }
      @-moz-keyframes rotation {
        from {-moz-transform: rotate(0deg);}
        to {-moz-transform: rotate(360deg);}
      }
      @-o-keyframes rotation {
        from {-o-transform: rotate(0deg);}
        to {-o-transform: rotate(360deg);}
      }
      @keyframes rotation {
        from {transform: rotate(0deg);}
        to {transform: rotate(360deg);}
      }

    </style>

    <link rel="prefetch" href="./vtx.js">
    <link rel="prefetch" href="https://pygame-web.github.io/archives/0.9/vt/xterm.js">
    <link rel="prefetch" href="https://pygame-web.github.io/archives/0.9/vt/xterm-addon-image.js">

    <script src="tinytar.min.js"></script>

<script type=module>
#if MODULARIZE
    console.log(" ============= ES6 MODULE ============= ");
    import initModule from "./postgres.js";
    window.initModule = initModule //(Module)
#endif
</script>
</head>

<body>
    <hr/>
    <figure style="overflow:visible;" id="spinner"><div class="spinner"></div><center style="margin-top:0.5em"><strong>emscripten</strong></center></figure>
    <div class="emscripten" id="status">Downloading...</div>
    <div class="emscripten">
      <progress value="0" max="100" id="progress" hidden=1></progress>
    </div>

    <div class="emscripten_border" hidden>
      <canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()" tabindex=-1></canvas>
    </div>

    <hr/>
    <div class="emscripten" hidden>
      <input type="checkbox" id="resize">Resize canvas
      <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer
      &nbsp;&nbsp;&nbsp;
      <input type="button" value="Fullscreen" onclick="Module.requestFullscreen(document.getElementById('pointerLock').checked,
                                                                                document.getElementById('resize').checked)">
    </div>

    <div id="repl"></div>

    <hr/>
    <textarea class="emscripten" id="output" rows="25" ></textarea>
    <hr>

<script type='text/javascript' defer>
    var statusElement = document.getElementById('status');
    var progressElement = document.getElementById('progress');
    var spinnerElement = document.getElementById('spinner');

    window.buffer_stdout = ""
    window.flushed_stdout = false

    async function boot() {


        function fnc_stdout(code) {

            var flush = (code == 4)

            if (flush) {
                flushed_stdout = true
            } else {
                if (code == 10) {
                    if (flushed_stdout) {
                        flushed_stdout = false
                        return
                    }

                    buffer_stdout += "\r\n";
                    flush = true
                }
                flushed_stdout = false
            }

            if (buffer_stdout != "") {
                if (flush) {
                    if (buffer_stdout.startsWith(sixel_prefix)) {
                        console.info("[sixel image]");
                        vm.vt.sixel(buffer_stdout);
                    } else {
                        if (buffer_stdout.startsWith("Looks like you are rendering"))
                            return;

                        vm.vt.xterm.write( b_utf8(buffer_stdout) )
                    }
                    buffer_stdout = ""
                    return
                }
            }
            if (!flush)
                buffer_stdout += String.fromCharCode(code);
        }


        function fnc_stderr(c) {
            if (!stderr_on) {
                stderr_on = true
                vm.vt.xterm.write("\x1B[1;3;31m")
            }
            if (c==10) {
                if (stderr_on) {
                    stderr_on = false
                    vm.vt.xterm.write('\x1B[0m')
                }
                vm.vt.xterm.write("\r\n")
            } else
                vm.vt.xterm.write( String.fromCharCode(c) )
        }

       function fnc_stdin() {
            if (test_data.length>0) {
                const c = test_data.shift()
                return c
            }
    // should never happen, blocking !
            console.error("pump", "EOF")
            return null;
        }

        window.stderr_on = false
        window.fnc_stdout = fnc_stdout
        window.fnc_stderr = fnc_stderr
        window.fnc_stdin = fnc_stdin


        window.test_step = 0
        window.test_data = []



        const text_codec = new TextDecoder()
        const sixel_prefix = String.fromCharCode(27)+"Pq"

        function b_utf8(s) {
            var ary = []
            for ( var i=0; i<s.length; i+=1 ) {
                ary.push( s.substr(i,1).charCodeAt(0) )
            }
            return text_codec.decode(  new Uint8Array(ary) )
        }
        window.b_utf8 = b_utf8

        function test_drive(step) {
            var data = null
            switch (test_step) {
                case 0:
                    data = "SHOW client_encoding;";
                    break

                case 1:
                    data = "SELECT now();"
                    break

                case 2:
                    //data = "CREATE EXTENSION vector;"
                    data = "LISTEN template1;"
                    break

                case 3:
                    // data = "SELECT * FROM pg_extension;"
                    data = "NOTIFY template1, 'hello';"

                    break
            }

            if (data!=null) {
                test_step++
                console.log("SQL:", data)
                vm.readline(data+"\n\n")
                setTimeout(test_drive, 1500);
            } else {
                console.log("SQL:End")
            }

        }

        const PGDATA="/tmp/pglite/base"

        // []
        function prerun(vm) {
            console.log("prerun(js)")
            vm.FS.mkdir(PGDATA)
            vm.FS.mount(vm.FS.filesystems.IDBFS, {autoPersist: true}, PGDATA)
        }


        function fs_callback(err) {
            if (!err) {
                if (vm.FS.filesystems.PGFS && vm.FS.filesystems.PGFS.load_package) {
                    vm.FS.filesystems.PGFS.load_package("vector","vector.tar.gz")
                    if (vm.FS.analyzePath(PGDATA+"/PG_VERSION").exists) {
                        console.log("@@@@@@@@@@@@@@@@@@@@@ found DB @@@@@@@@@@@@@@@@@@@@@@@")
                        console.log( vm.FS.readdir(PGDATA));
                        console.log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
                    } else {
                        console.warn("@@@@ no db @@@@");
                    }
                }
                setTimeout(delayed_init, 0)
            } else
                console.error("fs callback : FS error", err)
        }

        async function delayed_init() {
            /*
            Module.pg_extensions = {"vector": some_tar_as_a_blob }
            for (const ext in Module.pg_extensions) {
                var blob;
                try {
                    blob = await Module.pg_extensions[ext]
                } catch (x) {
                    console.error("failed to fetch extension :", ext)
                    continue
                }
                if (blob) {
                    const bytes = new Uint8Array(await blob.arrayBuffer())
                    console.log("  +", ext,"tardata:", bytes.length )
                    if (ext=="quack")
                       console.warn(ext,"skipped !")
                    else
                       PGFS.load_pg_extension(ext, bytes)
                } else {
                   console.error("could not get binary data for extension :", ext);
                }
            }
            */
            await Module.load_package("vector", "vector.tar.gz")

            vm._setenv(vm.stringToUTF8OnStack("REPL"),vm.stringToUTF8OnStack("Y"),1)

            vm._pg_initdb()

            vm._pg_repl_raf()

            setTimeout(test_drive, 2400)
        }

        // []
        function postrun(vm) {
            console.log("postrun(js)")
            vm.FS.syncfs(true, fs_callback)
        }


        // Module.load_package
        async function load_package(ext, url) {
                var bytes;
                var response;
                if (Module.FS.analyzePath(url).exists) {
                    console.error("PGFS TODO: handle local archives", url)
                } else {
                    console.warn("PGFS Fetching:", url)
                    response = await fetch(url);
                }

                if (url.endsWith(".tar")) {
                    const buffer = await response.arrayBuffer();
                    bytes = new Uint8Array(buffer);
                } else {
                   const ds = new DecompressionStream("gzip");
                   const gzbytes = await response.blob();
                     console.log("gzdata", gzbytes.size);
                     const stream_in = gzbytes.stream().pipeThrough(ds);
                     bytes = new Uint8Array(await new Response(stream_in).arrayBuffer());
                }
                Module.load_pg_extension(ext, bytes);
        }

        // Module.load_pg_extension
        function load_pg_extension(ext, bytes) {
            var data = tinyTar.untar(bytes);
            data.forEach(function(file) {
              if (!file.name.startsWith(".")) {
                  const _file = Module.WASM_PREFIX + "/" + file.name;
                  console.log("    + ", _file);
                  if (file.name.endsWith(".so")) {
                    console.warn(_file, "scheduled for wasm streaming compilation");

                    const ext_ok = (...args) => {
                      console.log("pgfs:ext OK", _file, args);
                    };

                    const ext_fail = (...args) => {
                        console.log("pgfs:ext FAIL", _file, args);
                    };
                    function dirname(p) {
                        const last = p.lastIndexOf("/");
                        if (last>0)
                            return p.substr(0,last)
                        return p;
                    }
                    Module.FS.createPreloadedFile(dirname(_file), file.name.split("/").pop(), file.data, true, true, ext_ok, ext_fail, false);
                    console.log("createPreloadedFile called for :", _file);
                  } else {
                    Module.FS.writeFile(_file, file.data);
                  }
              }
            });
            console.warn("pgfs ext:end", ext);
        }

        function copyFrom(filename, is_program) {
            console.warn("copyFrom:", filename, is_program);
        }
        function copyTo(filename, is_program) {
            console.warn("copyTo:", filename, is_program);
        }

        function copyToEnd() {
            console.warn("copyToEnd");
        }

        var Module = {

            WASM_PREFIX : "/tmp/pglite",
            PGDATA : "/tmp/pglite/base",

            copyFrom : copyFrom,
            copyTo  : copyTo,

            load_pg_extension: load_pg_extension,

            load_package: load_package,

            arguments : ["PGDATA=/tmp/pglite/base", "PREFIX=/tmp/pglite", "REPL=N"],

            config : {
                cdn : "https://pygame-web.github.io/archives/0.9/",
            },

            print: (()=>{
              var element = document.getElementById('output');
              if (element) element.value = ''; // clear browser cache
              return (...args) => {
                var text = args.join(' ');
                // These replacements are necessary if you render to raw HTML
                //text = text.replace(/&/g, "&amp;");
                //text = text.replace(/</g, "&lt;");
                //text = text.replace(/>/g, "&gt;");
                //text = text.replace('\n', '<br>', 'g');
                console.log(text);
                if (element) {
                  element.value += text + "\n";
                  element.scrollTop = element.scrollHeight; // focus on bottom
                }
              };

            })(),

            canvas: (() => {
              var canvas = document.getElementById('canvas');

              // As a default initial behavior, pop up an alert when webgl context is lost. To make your
              // application robust, you may want to override this behavior before shipping!
              // See http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
              canvas.addEventListener("webglcontextlost", (e) => { alert('WebGL context lost. You will need to reload the page.'); e.preventDefault(); }, false);

              return canvas;
            })(),
    /*
            stdin : fnc_stdin,
    */
            stdout : fnc_stdout,
            stderr : fnc_stderr,

            preRun : [ prerun ],
            postRun : [ postrun ],

            locateFile : function(path, prefix) {
                console.log("locateFile: "+path+' '+prefix);
                return prefix + path;
            },

            setStatus: (text) => {
              if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' };
              if (text === Module.setStatus.last.text) return;
              var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/);
              var now = Date.now();
              if (m && now - Module.setStatus.last.time < 30) return; // if this is a progress update, skip it if too soon
              Module.setStatus.last.time = now;
              Module.setStatus.last.text = text;
              if (m) {
                text = m[1];
                progressElement.value = parseInt(m[2])*100;
                progressElement.max = parseInt(m[4])*100;
                progressElement.hidden = false;
                spinnerElement.hidden = false;
              } else {
                progressElement.value = null;
                progressElement.max = null;
                progressElement.hidden = true;
                if (!text) spinnerElement.hidden = true;
              }
              statusElement.innerHTML = text;
            },
            totalDependencies: 0,
            monitorRunDependencies: (left) => {
              this.totalDependencies = Math.max(this.totalDependencies, left);
              Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.');
            },

            readline : function(line) {
                const ud = { "type" : "stdin", "data" : line }
                if (window.worker) {
                    window.worker.postMessage({ target: 'custom', userData: ud });
                } else {
                    window.Module.postMessage(ud);
                }
            }
          };



          Module.setStatus('Downloading...');
          window.onerror = () => {
            Module.setStatus('Exception thrown, see JavaScript console');
            spinnerElement.style.display = 'none';
            Module.setStatus = (text) => {
              if (text) console.error('[post-exception status] ' + text);
            };
          };

        try {
            console.log(initModule)
            globalThis.window.is_es6 = true
        } catch (x) {
            globalThis.window.is_es6 = false

        }
        console.warn("ES6 ?", globalThis.window.is_es6)

        function jsimport(url, sync) {
            const jsloader=document.createElement('script')
            jsloader.setAttribute("type","text/javascript")
            jsloader.setAttribute("src", url)
            if (!sync)
                jsloader.setAttribute('async', true);
            document.head.appendChild(jsloader)
        }

        jsimport("tinytar.min.js")

        window.vm = Module

        const { WasmTerminal } = await /**/ import("./vtx.js")

        Module.vt = new WasmTerminal("repl",132,32,12)
#if MODULARIZE
        if (is_es6)
            window.Module = await initModule(Module)
        else
#endif
        {
            window.Module = Module
            jsimport("postgres.js")
            console.log("main:", Module )
        }

    }

    window.addEventListener("load", boot )

</script>

<!--
    {{{ SCRIPT }}}
-->
  </body>
</html>
